package edu.upenn.cis350.algoviz;

import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.graphics.Bitmap;

import java.util.ArrayList;

/**
 * View where user packs objects into bins.
 */

public class BinPackingView extends View {

	///// Static variables /////
	private static BinPackingProblemFactory factory;
	private static ArrayList<BinObject> objects = new ArrayList<BinObject>();
	private static ArrayList<Bin> bins = new ArrayList<Bin>();
	private static int[] images = new int[]{R.drawable.baby, R.drawable.bike, R.drawable.book, R.drawable.ipod, 
		R.drawable.cake, R.drawable.camera, R.drawable.cash, R.drawable.cat, R.drawable.clock, R.drawable.lamp, 
		R.drawable.laptop, R.drawable.milk, R.drawable.ottoman, R.drawable.passport, R.drawable.ring, R.drawable.teddybear, 
		R.drawable.toast, R.drawable.tv, R.drawable.wallet, R.drawable.xmastree, R.drawable.tiger};


	private static int[] images_small = new int[]{R.drawable.baby_icon, R.drawable.bike_icon, R.drawable.book_icon, R.drawable.ipod_icon, 
		R.drawable.cake_icon, R.drawable.camera_icon, R.drawable.cash_icon, R.drawable.cat_icon, R.drawable.clock_icon, R.drawable.lamp_icon, 
		R.drawable.laptop_icon, R.drawable.milk_icon, R.drawable.ottoman_icon, R.drawable.passport_icon, R.drawable.ring_icon, R.drawable.teddybear_icon, 
		R.drawable.toast_icon, R.drawable.tv_icon, R.drawable.wallet_icon, R.drawable.xmastree_icon, R.drawable.tiger_icon};

	private static BinObject objToMove = null;
	private static final int TEXT_X_OFFSET = 10;
	private static final int TEXT_Y_OFFSET = 25;
	private static final int TEXT_Y_BINOBJ_OFFSET = 10;
	private static float xClick, yClick;

	///// Instance variables /////
	private boolean reset;
	private int current_value;

	private BinObjectPaginator _currentPaginator, _unallocatedObjectsPaginator;

	private int binWidth;
	private int binHeight;
	private int objWidth;
	private int objHeight;


	///// Constructors /////
	public BinPackingView(Context c) {
		super(c);
		BinPackingView.factory = new BinPackingProblemFactory(c);
		reset=true;
	}

	public BinPackingView(Context c, AttributeSet a) {
		super(c,a);
		BinPackingView.factory = new BinPackingProblemFactory(c);
		reset=true;
	}


	///// Public methods /////

	/**
	 * Initialize the app by fetching the list of Bins and BinObjects from
	 * the BinPackingProblemFactory instantiated in the constructor.  Also,
	 * set the size of each object to some percentage of the screen size.
	 */
	public void initialize() {

		objects = new ArrayList<BinObject>();
		bins = new ArrayList<Bin>();

		BinPackingActivity currentContext=(BinPackingActivity) this.getContext();
		String problemName = currentContext.getProblemName();
		bins.addAll(factory.getBins(problemName));
		objects.addAll(factory.getBinObjects(problemName));
		// Location of center of View
		int mid = this.getWidth() / 2;

		// Specify sizes of Bins and BinObjects relative to size of screen
		//TODO: binWidth = (int)(0.30 * this.getWidth());
		//binWidth = (int)(this.getWidth());
		//TODO: binHeight = (int)(0.15 * this.getHeight());
		binHeight = (int)(0.25*this.getHeight());
		objWidth = (int)(0.15 * this.getWidth());
		objHeight = objWidth;

		int bin_loc_offset=100;

		// Set instances of Bins and BinObjects to appropriate size
		/*	for (Bin bin : bins) {
			bin.setWidth(binWidth);
			bin.setHeight(binHeight);
		}*/
		for (BinObject obj : objects) {
			obj.setWidth(objWidth);
			obj.setHeight(objHeight);
			Bitmap image = BitmapFactory.decodeResource(getResources(), images[(int)obj.getImageID()]).copy(Bitmap.Config.ARGB_8888, true);
			Bitmap image_small = BitmapFactory.decodeResource(getResources(), images_small[(int)obj.getImageID()]).copy(Bitmap.Config.ARGB_8888, true);
			obj.setBitmap(image);
			obj.setBitmap_small(image_small);
		}

		// Initialize the paginator and set it to be the current one
		_unallocatedObjectsPaginator = new BinObjectPaginator(mid, objWidth, objHeight, "Unallocated Objects");
		_unallocatedObjectsPaginator.addAll(objects);		
		_currentPaginator = _unallocatedObjectsPaginator;




		// Set locations for all Bins
		Bin b1, b2, b3;
		switch (BinPackingView.bins.size()) {
		case 1:
			binWidth = this.getWidth();
			b1 = BinPackingView.bins.get(0);
			b1.instantiatePaginator(mid, "Middle Bin");
			//TODO: b1.setX(mid - binWidth / 2);
			b1.setX(0);
			//TODO: b1.setY(this.getHeight() - binHeight-bin_loc_offset);
			b1.setY(this.getHeight()-binHeight);
			break;
		case 2:
			binWidth = this.getWidth()/2;
			b1 = BinPackingView.bins.get(0);
			b1.instantiatePaginator(mid, "Left Bin");
			b2 = BinPackingView.bins.get(1);
			b2.instantiatePaginator(mid, "Right Bin");
			//			b1.setX((int)(mid - binWidth - 0.015 * this.getWidth()));
			//			b1.setY(this.getHeight() - binHeight-bin_loc_offset);
			//			b2.setX((int)(mid + 0.015 * this.getWidth()));
			//			b2.setY(this.getHeight() - binHeight-bin_loc_offset);
			b1.setX(0);
			b1.setY(this.getHeight()- binHeight);
			b2.setX(binWidth);
			b2.setY(this.getHeight()- binHeight);


			break;
		case 3:
			binWidth = this.getWidth()/3;
			b1 = BinPackingView.bins.get(0);
			b1.instantiatePaginator(mid, "Left Bin");
			b2 = BinPackingView.bins.get(1);
			b2.instantiatePaginator(mid, "Middle Bin");
			b3 = BinPackingView.bins.get(2);
			b3.instantiatePaginator(mid, "Right Bin");
			b1.setX(0);
			b2.setX(binWidth);
			b3.setX(binWidth*2);
			b1.setY(this.getHeight() - binHeight);
			b2.setY(this.getHeight() - binHeight);
			b3.setY(this.getHeight() - binHeight);
			break;
		default:
			Log.e("Bin number error", "Number of bins must be 1, 2, or 3!");
			break;
		} 
		//setting the Bin locations.
		for (Bin bin : bins) {
			bin.setWidth(binWidth);
			bin.setHeight(binHeight);
		}
		// Initialize the total value to 0
		current_value = 0;

	}

	protected void onDraw(Canvas c) {
		// If drawing for the first time, get bins and objects from factory
		// and set their locations using initialize()		
		if (reset==true) {
			initialize();
			reset=false;
		}
		drawLevel(c);
	}

	protected void drawLevel(Canvas canvas){

		drawBins(canvas);
		drawPaginator(canvas);
		drawObjects(canvas);
	}


	private void drawBins(Canvas canvas) {
		int fill_height;
		Paint paint = new Paint();



		for (Bin bin : bins) {
			//start afresh
			paint.reset();
			paint.setTextSize(30);

			paint.setStyle(Paint.Style.FILL);
			//	paint.setStyle(Paint.Style.STROKE);
			//paint.setStrokeWidth(5);
			paint.setColor(Color.WHITE);
			paint.setAlpha(130);
			//canvas.drawRect(bin.getX(), bin.getY()-10, bin.getX() + binWidth, bin.getY() + binHeight, paint);
			canvas.drawRect(bin.getX(), bin.getY() , bin.getX() + binWidth, this.getHeight(), paint);
			paint.setColor(bin.getColor());

			paint.setStyle(Paint.Style.FILL);
			fill_height = (int)((bin.getHeight()) * bin.getWeight() / bin.getCapacity());
			//TODO:
			//canvas.drawRect(bin.getX()+2, bin.getY() + 2+binHeight - fill_height,
			//		bin.getX()-2 + binWidth, bin.getY()-2 + binHeight, paint);
			canvas.drawRect(bin.getX(), bin.getY() + binHeight - fill_height, bin.getX() + binWidth, bin.getY() + binHeight, paint);
			if (bin.getText().indexOf('C') == -1)
				throw new UnsupportedOperationException("Unexpected Bin Text");
			String text1 = bin.getText().substring(0,bin.getText().indexOf('C'));
			String text2 = bin.getText().substring(bin.getText().indexOf('C'));

			paint.setColor(Color.BLACK);
			canvas.drawText(text1, bin.getX() + TEXT_X_OFFSET, bin.getY() + binHeight/2, paint);
			canvas.drawText(text2, bin.getX() + TEXT_X_OFFSET, bin.getY() + binHeight/2 + TEXT_Y_OFFSET, paint);

			paint.setStyle(Paint.Style.STROKE);
			paint.setStrokeWidth(2);
			canvas.drawRect(bin.getX(), bin.getY() , bin.getX() + binWidth, this.getHeight(), paint);

		}
		paint.setStyle(Paint.Style.FILL);
		for (Bin bin : bins){

			for (BinObject obj : bin.getContents()) {
				//				obj.setX(obj.getX());
				//				obj.setY(obj.getY());
				paint.setStyle(Paint.Style.FILL);
				if (obj.getX() < 2) obj.setX(2);
				if (obj.getX() > this.getWidth()-70 ) obj.setX(this.getWidth()-70);
				if (obj.getY() < 3) obj.setY(3);
				if (obj.getY() > this.getHeight()-70 ) obj.setY(this.getHeight() -70);
				paint.setTextSize(20);
				canvas.drawBitmap(obj.getBitmap_small(), obj.getX(), obj.getY(), paint);
				paint.setColor(Color.WHITE);
				if (obj.getText().indexOf('$') == -1)
					throw new UnsupportedOperationException("Unexpected Object Text");
				String t1 = obj.getText().substring(0,obj.getText().indexOf('$'));
				String t2 = obj.getText().substring(obj.getText().indexOf('$'));
				paint.setShadowLayer(2, 3, 3, Color.BLACK);
				paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
				canvas.drawText(t1, obj.getX() + TEXT_X_OFFSET, obj.getY() + objHeight/3 , paint);
				canvas.drawText(t2, obj.getX() + TEXT_X_OFFSET, obj.getY() + objHeight/3 + TEXT_Y_OFFSET, paint);
				
				// check if obj isInSolution is true
				paint.setColor(Color.GREEN);
				paint.setStyle(Paint.Style.STROKE);
				if (obj.getIsInSolution()) {
					canvas.drawCircle(obj.getX()+obj.getWidth()/3, obj.getY()+obj.getHeight()/3, obj.getHeight()/3, paint);
				}
			}
		}


	}

	private void drawPaginator(Canvas canvas) {
		
	
		
		Paint paint = new Paint();

		paint.setColor(_currentPaginator.getColor());	
		float xPos = _currentPaginator.getX(), yPos = _currentPaginator.getY(),
				width =  _currentPaginator.getWidth(), height = _currentPaginator.getHeight();		
		canvas.drawRect(xPos, yPos, xPos + width, yPos + height, paint);

		paint.setColor(Color.WHITE);
		paint.setTextSize(14);
		String title = _currentPaginator.getTitle();
		canvas.drawText(title, xPos + width/2 - title.length()/2 * 6, yPos, paint);

		String currentPage = _currentPaginator.getText();
		//		if (!currentPage.endsWith("1")) {
		//			canvas.drawText(currentPage, xPos + width/2 - currentPage.length()/2 * 6, yPos+height-10, paint);
		//			canvas.drawText("Next Page >>", width/2 + 80, yPos+height-10, paint);
		//			canvas.drawText("<< Previous Page", width/2 - 80 - 16*6, yPos+height-10, paint);}
		//		

		if (!_currentPaginator.equals(_unallocatedObjectsPaginator)){ 
			paint.setTextSize(30);
			paint.setColor(Color.BLACK);
			canvas.drawRect(xPos+width-80, yPos, xPos + width-50, yPos+30, paint);
			paint.setColor(Color.RED);
			canvas.drawText("X", width-74, yPos+28, paint);
		}

	}

	private void drawObjects(Canvas canvas) {
		Paint paint = new Paint();
		paint.setTextSize(30);

		int color_num=0;
		for (BinObject obj : _currentPaginator.getCurrentPageObjects()) {

			paint.setColor(obj.getColor());
			color_num++;
			if (color_num>7)
				color_num=0;

			if (obj.getX() < 3) obj.setX(3);
			if (obj.getX() > this.getWidth()-100 ) obj.setX(this.getWidth()-100);
			if (obj.getY() < 3) obj.setY(3);
			if (obj.getY() > this.getHeight()-100 ) obj.setY(this.getHeight() -100);

			canvas.drawBitmap(obj.getBitmap(), obj.getX(), obj.getY(), paint);
			//canvas.drawRect(obj.getX(), obj.getY(), obj.getX() + objWidth, obj.getY() + objHeight, paint);
			paint.setColor(Color.WHITE);
			if (obj.getText().indexOf('$') == -1)
				throw new UnsupportedOperationException("Unexpected Object Text");
			String text1 = obj.getText().substring(0,obj.getText().indexOf('$'));
			String text2 = obj.getText().substring(obj.getText().indexOf('$'));
			paint.setShadowLayer(2, 3, 3, Color.BLACK);
			paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
			canvas.drawText(text1, obj.getX() + TEXT_X_OFFSET, obj.getY() + objHeight/2, paint);
			canvas.drawText(text2, obj.getX() + TEXT_X_OFFSET, obj.getY() + objHeight/2 + TEXT_Y_OFFSET, paint);
			
			// check if isInSolution is true - if so, draw circle
			paint.setColor(Color.GREEN);
			paint.setShadowLayer(0, 0, 0, 0);
			paint.setStyle(Paint.Style.STROKE);
			if (obj.getIsInSolution()) {
				canvas.drawCircle(obj.getX()+obj.getWidth()/2, obj.getY()+obj.getHeight()/2, obj.getHeight()/2, paint);
			}
			
		}
	}

	public void reset(){
		reset = true;
		BinObject[] objsInBin;
		for (Bin bin : bins) {
			objsInBin = new BinObject[bin.getContents().size()];
			for (int i = 0; i < objsInBin.length; i++) {
				objsInBin[i] = bin.getContents().get(i);
			}
			for (int i = 0; i < objsInBin.length; i++) {
				bin.remove(objsInBin[i]);
			}
		}
		
		for (BinObject obj : objects) {
			obj.setIsInSolution(false);
		}
		invalidate();
	}

	public boolean onTouchEvent(MotionEvent event) {
		float x = event.getX(), y = event.getY();
		int action = event.getAction();

		if (action == MotionEvent.ACTION_DOWN) return handleClick(x, y);
		else if (action == MotionEvent.ACTION_MOVE) return handleDrag(x, y);
		else if (action == MotionEvent.ACTION_UP) return handleDrop(x, y);
		else return false;	
	}

	/**
	 * Handles ACTION_DOWN motion events.
	 * @param x The x-coordinate of the touch event.
	 * @param y The y-coordinate of the touch event.
	 * @return True if object or bin is clicked, false otherwise.
	 */
	private boolean handleClick(float x, float y) {
				
		//check if a object is selected
		for (BinObject obj : _currentPaginator.getCurrentPageObjects()) {
			if (obj.containsPoint(x, y)) {
				obj.setXDiff(x - obj.getX());
				obj.setYDiff(y - obj.getY());
				objToMove = obj;
				xClick = objToMove.getX();
				yClick = objToMove.getY();
				invalidate();
				return true;
			}
		}
		objToMove = null;

		for (Bin bin : bins) {
			for (BinObject obj: bin.getContents()) {
				if (obj.containsPoint(x, y)) {
					obj.setXDiff(x - obj.getX());
					obj.setYDiff(y - obj.getY());
					objToMove = obj;
					xClick = objToMove.getX();
					yClick = objToMove.getY();
					invalidate();
					return true;
				}
			}	
		}

		float xPos = _currentPaginator.getX(), yPos = _currentPaginator.getY(),
				width =  _currentPaginator.getWidth(), height = _currentPaginator.getHeight();

		invalidate();

		return false;
	}

	private boolean handleDrag(float x, float y) {
		if (objToMove == null) return true;
		objToMove.setX((float)(x - objToMove.getXDiff()));
		objToMove.setY((float)(y - objToMove.getYDiff()));
		invalidate();
		return true;
	}

	private void resetObjXY(){
		objToMove.setX(objToMove.oldx);
		objToMove.setY(objToMove.oldy);	
	}

	/**
	 * Handles ACTION_UP motion events.
	 * @param x The x-coordinate of the touch event.
	 * @param y The y-coordinate of the touch event.
	 * @return
	 */
	private boolean handleDrop(float x, float y) {
		//		if (x < 3) x = 3;
		//		if (x > this.getWidth()-100 ) x= this.getWidth()-100;
		//		if (y < 3) y=3;
		//		if (y > this.getHeight()-100 ) y = this.getHeight() -100;
		//		
		if (objToMove == null) return false;
		else objToMove.setColor(objToMove.getColor());
		Bin currentBin = null;
	
		for(Bin bin: bins) if(bin.getContents().contains(objToMove)) currentBin = bin;

		// If object collides with a different bin, update the value
		boolean collided = false;

		for (Bin bin : bins) {

			if (objToMove.collidesWith(bin)) {
				if (!collided) {
					float objX= objToMove.getX();
					float objY =objToMove.getY();
					int objHeight =objToMove.getHeight();
					int objWidth = objToMove.getWidth();

					collided = true;
					if (!bin.getContents().contains(objToMove)) {
						boolean inserted = bin.insert(objToMove);
						if (inserted) {
							if (currentBin != null) currentBin.remove(objToMove);
							else {
								_unallocatedObjectsPaginator.remove(objToMove);

								//		objToMove.setX((int)x);
								//			objToMove.setY((int)y);
								
								objToMove.setX(objX);
								objToMove.setY(objY);
								
								int binHeight = bin.getHeight();
								int binWidth = bin.getWidth();
								float binX = bin.getX();

								//if object is to the left of the appropriate bin
								if(objX < binX){
									objToMove.setX(binX + objWidth/2); 
								}
								else if(objX > (binX + binWidth-objWidth/2)){
									objToMove.setX(binX + binWidth - objWidth/2);
								}
								if(objY < (this.getHeight() - binHeight)){
									objToMove.setY(this.getHeight()-binHeight); 
								}
								else if(objY > this.getHeight()){
									objToMove.setY(this.getHeight() - 5);
								}
	
							}
						} else {
							objToMove.setX(xClick);
							objToMove.setY(yClick);
							//resetObjXY();
							
						}
					}else{
						
						objToMove.setX(objX);
						objToMove.setY(objY);
						
						int binHeight = bin.getHeight();
						int binWidth = bin.getWidth();
						float binX = bin.getX();

						//if object is to the left of the appropriate bin
						if(objX < binX){
							objToMove.setX(binX + objWidth/2); 
						}
						else if(objX > (binX + binWidth - objWidth/2)){
							objToMove.setX(binX + binWidth - objWidth/2);
						}
						if(objY < (this.getHeight() - binHeight)){
							objToMove.setY(this.getHeight()-binHeight); 
						}
						else if(objY > this.getHeight()){
							objToMove.setY(this.getHeight() - 5);
						}
					}

				} else {
					
				}
			}

		}
		if (!collided) {
			//			objToMove.setX((int)x);
			//			objToMove.setY((int)y);
			if(currentBin != null){
				currentBin.remove(objToMove);
				_unallocatedObjectsPaginator.add(objToMove);

			}

		}
		invalidate();
		return true;

	}


	//when the user press "done" button
	public double submit(){
		//calculate the current value
		current_value=0;
		for (Bin bin : bins) 
			current_value += bin.getValue(); 	


		Double optimalSol=BinPackingView.factory.getOptimalSolution(((BinPackingActivity) this.getContext()).getProblemName());

		if (optimalSol<=current_value){
			return 1.1;
		}
		else{
			//supposedly if the user get a close answer, should show a toast
			double percent=current_value/optimalSol;
			if (percent>=0.9)
				return 0.9;
			else 
				if (percent>=0.75)
					return 0.75;
				else 
					if (percent>=0.5)
						return 0.5;
					else 
						if (percent>=0.3)
							return 0.3;
						else
							return 0;
		}

	}

	public BinPackingProblemFactory getFactory() {
		return factory;
	}

	public void solution() {
		BinPackingActivity currentContext=(BinPackingActivity) this.getContext();
		String problemName = currentContext.getProblemName();
		
		if (problemName.equals("Baby Packer")) {
			for (BinObject obj : objects) {
				if (obj.getValue()==10 && obj.getWeight()==5) {
					obj.setIsInSolution(true);
				} else if (obj.getValue()==32 && obj.getWeight()==17) {
					obj.setIsInSolution(true);
				} else if (obj.getValue()==11 && obj.getWeight()==3) {
					obj.setIsInSolution(true);
				}
			}
		} else if (problemName.equals("Pack Rat")){
			for (BinObject obj : objects) {
				if (obj.getValue()==4 && obj.getWeight()==3) {
					obj.setIsInSolution(true);
				} else if (obj.getValue()==9 && obj.getWeight()==4) {
					obj.setIsInSolution(true);
				} else if (obj.getValue()==20 && obj.getWeight()==8) {
					obj.setIsInSolution(true);
				} else if (obj.getValue()==20 && obj.getWeight()==10) {
					obj.setIsInSolution(true);
				}
			}
		} else if (problemName.equals("Experienced Packer")) {
			for (BinObject obj : objects) {
				if (obj.getValue()==4 && obj.getWeight()==3) {
					obj.setIsInSolution(true);
				} else if (obj.getValue()==11 && obj.getWeight()==3) {
					obj.setIsInSolution(true);
				} else if (obj.getValue()==20 && obj.getWeight()==8) {
					obj.setIsInSolution(true);
				} else if (obj.getValue()==10 && obj.getWeight()==5) {
					obj.setIsInSolution(true);
				}
			}
		}
		invalidate();
	}

} 
